﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using COFE.Core.Filter;
using System.Linq.Expressions;
using COFE.Core;
using COFE.Core.Validater;

namespace COFE.Data.Filter
{
    public class SortResultOptionFilterExpression :
        OptionFilterExpression<COFE.Core.Filter.SortResultOptionFilter>
    {
        public SortResultOptionFilterExpression()
            : base()
        {

        }

        private IQueryable<Entry>
            orderBy<T>(
            IQueryable<Entry> input,
            SortDirectionType sortDirection,
            params Expression<Func<Entry, T>>[] exp
            )
        {
            IOrderedQueryable<Entry> retVal;
            if (exp.Length == 0)
                throw new ArgumentException();

            if (sortDirection == SortDirectionType.Ascending)
                retVal = input.OrderBy(exp[0]);
            else retVal = input.OrderByDescending(exp[0]);

            for (int i = 1; i < exp.Length; i++)
                if (sortDirection == SortDirectionType.Ascending)
                    retVal = retVal.ThenBy(exp[i]);
                else retVal = retVal.ThenByDescending(exp[i]);

            return retVal;
        }

        private IEnumerable<Entry>
           orderBy<T>(
           IEnumerable<Entry> input,
           SortDirectionType sortDirection,
           params Func<Entry, T>[] exp
           )
        {
            IEnumerable<Entry> retVal;
            if (exp.Length == 0)
                throw new ArgumentException();

            if (sortDirection == SortDirectionType.Ascending)
                retVal = input.OrderBy(exp[0]);
            else retVal = input.OrderByDescending(exp[0]);

            for (int i = 1; i < exp.Length; i++)
                if (sortDirection == SortDirectionType.Ascending)
                    retVal = retVal.OrderBy(exp[i]);
                else retVal = retVal.OrderByDescending(exp[i]);

            return retVal;
        }


        public override IQueryable<Entry> ProcessQuery(
            IQueryable<Entry> input, IOptionFilter filter, IOptionFilter[] options)
        {
            var optionFilter = (COFE.Core.Filter.SortResultOptionFilter)filter;
                        
            SortDirectionType sd = optionFilter.SortDirection;            

            switch ((int)optionFilter.SortProperty)
            {
                case (int)FileSystemInfoProperties.Name:
                case (int)FileSystemInfoProperties.Label:
                    return orderBy(input, sd, e => e.Label);
                case (int)FileSystemInfoProperties.FullParseName:
                    return orderBy(input, sd, e => e.PrimaryVolumeId, e => e.ParsePath);
                case (int)FileSystemInfoProperties.CreationTimeUtc:
                    return orderBy(input, sd, e => e.CreationTimeUtc);
                case (int)FileSystemInfoProperties.LastWriteTimeUtc:
                    return orderBy(input, sd, e => e.LastWriteTimeUtc);
                case (int)FileSystemInfoProperties.LastAccessTimeUtc:
                    return orderBy(input, sd, e => e.LastAccessTimeUtc);
                case (int)FileInfoProperties.Length:
                    return orderBy(input, sd, e => (e as FileEntry).Length);
                case (int)DirectoryInfoProperties.FileCount:
                    return orderBy(input, sd, e => (e as FolderEntry).FileCount);
                case (int)DirectoryInfoProperties.SubFolderCount:
                    return orderBy(input, sd, e => (e as FolderEntry).SubFolderCount);
                default:
                    return input.Where((e) =>
                        e.EntryProperties.Any((ep) => 
                            ep.PropertyId == (int)optionFilter.SortProperty));
                // Sqlite throw exception : Apply Joins are not Supported.
                //http://system.data.sqlite.org/index.html/rptview?rn=1
                //    return input.
                //OrderBy(e =>
                //(e.EntryProperties.FirstOrDefault(
                //ep => ep.PropertyId == (int)FileInfoProperties.CRC)
                //as EntryProperty_String)
                //.Value);                                    
            }
        }

        public override IEnumerable<Entry> ProcessQueryOutput(IEnumerable<Entry> input, IOptionFilter filter, IOptionFilter[] options)
        {
            var optionFilter = (COFE.Core.Filter.SortResultOptionFilter)filter;

            SortDirectionType sd = optionFilter.SortDirection;

            switch ((int)optionFilter.SortProperty)
            {
                case (int)FileSystemInfoProperties.Name:
                case (int)FileSystemInfoProperties.Label:                    
                case (int)FileSystemInfoProperties.FullParseName:                    
                case (int)FileSystemInfoProperties.CreationTimeUtc:                    
                case (int)FileSystemInfoProperties.LastWriteTimeUtc:                    
                case (int)FileSystemInfoProperties.LastAccessTimeUtc:                    
                case (int)FileInfoProperties.Length:                    
                case (int)DirectoryInfoProperties.FileCount:                    
                case (int)DirectoryInfoProperties.SubFolderCount:
                    return base.ProcessQueryOutput(input, filter, options);
                default:
                    int propId = (int)optionFilter.SortProperty;
                    switch (optionFilter.SortPropertyType.ToString())
                    {
                        case "System.String" :
                            return orderBy(input, optionFilter.SortDirection,
                                (e) => (e.EntryProperties.FirstOrDefault(
                                    (ep) => ep.PropertyId == propId)
                                    as EntryProperty_String).Value);
                    }

                    return input;                
            }
            
            
        }

        //private static Func<IOptionFilter, IOptionFilter[],
        //    Expression<Func<Entry, bool>>> getExpressionConstructor()
        //{
        //    return
        //        (f, o) =>
        //        {
        //            var filter = f as COFE.Core.Filter.SortResultOptionFilter;
        //            System.Linq.Expressions.Expression<Func<Entry, bool>> exp = e => true;

        //            switch (filter.SortPropertyString.ToLower())
        //            {
        //                case "name" : 

        //            }
        //        };

    }
}
